
/**
 * @file NetworkObsStreams.cpp
 * This class synchronizes rinex observation data streams.
 */

#include "NetworkObsStreams.hpp"

using namespace std;
using namespace gpstk;
using namespace StringUtils;

#define debug 0

namespace gpstk
{
      // Add a rinex obs file to the network
      // @obsFile   Rinex observation file name
    bool NetworkObsStreams::addRinexObsFile(const std::string& obsFile)
    {
         // object to hold the data
        ObsData oData;
        oData.obsFile = obsFile;

        try
        {
            // allocate memory
            oData.pObsStream = new Rinex3ObsStream();

            if( !oData.pObsStream )
            {
                delete oData.pObsStream;
                return false;
            }

            // open the obs file
            oData.pObsStream->exceptions(std::ios::failbit);
            oData.pObsStream->open(oData.obsFile, std::ios::in);

            // read the header of the obs file
            Rinex3ObsHeader obsHeader;
            (*oData.pObsStream) >> obsHeader;

            oData.obsSource.type = SatIDsystem2SourceIDtype(obsHeader.fileSysSat);
            oData.obsSource.sourceName 
                = StringUtils::upperCase( obsHeader.markerName.substr(0,4));

            // store the data for the receiver
            obsDataList.push_back(oData);

            sourceStreamMap[oData.obsSource] = oData.pObsStream;

            allSourceSet.insert(oData.obsSource);

            return true;
        }
        catch(...)
        {
            // Problem opening the file

            // We have to deallocate the memory here
            delete oData.pObsStream;
            oData.pObsStream = (Rinex3ObsStream*)0;

            return false;
        }

    }  // End of method 'NetworkObsStreams::addRinexObsFile()'


     /// set rinex index file, which store all the rinex file names 
     /// @rnxIndexFile   Rinex index file name 
    void NetworkObsStreams::activateSourceSet(SourceIDSet sourceSet)
    {
        activeSourceSet = sourceSet;

        std::list<ObsData> activeObsList;

        for(std::list<ObsData>::iterator it = obsDataList.begin();
            it != obsDataList.end();
            ++it)
        {
            SourceID currSource = (*it).obsSource;

            std::string currSourceName = currSource.sourceName;

            for(SourceIDSet::iterator itsrc = activeSourceSet.begin();
                itsrc != activeSourceSet.end();
                ++itsrc)
            {
                std::string activeSourceName = (*itsrc).sourceName;
                if(currSourceName == activeSourceName)
                {
                    activeObsList.push_back((*it));
                }
            }
        }

        obsDataList.clear();
        obsDataList = activeObsList;

        std::map<SourceID, Rinex3ObsStream*> activeSrcStreamMap;
        for(std::map<SourceID,Rinex3ObsStream*>::iterator it = sourceStreamMap.begin();
            it != sourceStreamMap.end();
            ++it)
        {
            std::string srcName = (*it).first.sourceName;
            for(SourceIDSet::iterator itsrc = activeSourceSet.begin();
                itsrc != activeSourceSet.end();
                ++itsrc)
            {
                std::string activeName= (*itsrc).sourceName;
                if(srcName == activeName)
                {
                    activeSrcStreamMap[(*it).first] = (*it).second;
                }
            }
        }

        sourceStreamMap.clear();
        sourceStreamMap = activeSrcStreamMap;

    };


      // Add a rinex obs file to the network
      // @obsFile   Rinex observation file name
    bool NetworkObsStreams::setRinexIndexFile(const std::string& rnxIndexFile)
    {

        obsDataList.clear();
        sourceStreamMap.clear();

        ifstream inpf(rnxIndexFile.c_str());
        if(!inpf)
        {
           FileMissingException fme("Could not open rinex index file " + rnxIndexFile);
           GPSTK_THROW(fme);
        }

           // Now, read the leap second data
        bool ok (true);
        while(!inpf.eof() && inpf.good())
        {
           string line;
           getline(inpf,line);
           StringUtils::stripTrailing(line,'\r');

           if( inpf.eof() ) break;
           if( inpf.bad() ) break;

           if(debug)
           {
              cout << "line:" << line << endl;
           }

              // Now, let's add this rinex file into 'network streams'
           addRinexObsFile(line);

        }

        inpf.close();

        return true;

        if(!ok)
        {
           FileMissingException fme("rinex index File " + rnxIndexFile
                                    + " is corrupted or wrong format");
           GPSTK_THROW(fme);

           return false;
        }
    }  // End of method 'NetworkObsStreams::setRinexIndexFile()'

      // Get epoch data of the network
      // @epoch     Epoch
      // @gData     Object hold epoch observation data of the network
    void NetworkObsStreams::readEpochData( const CommonTime& epoch,
                                           commonHeader& comHeader,
                                           sourceRinex& gData )
    {

        pAntexReader = comHeader.pAntexReader;

        // clear the data map
        gData.body.clear();

        SourceIDSet sourceRejectedSet;

        for(std::map<SourceID,Rinex3ObsStream*>::iterator it = sourceStreamMap.begin();
            it != sourceStreamMap.end();
            ++it)
        {
            gnssRinex gRin;

            SourceID source( it->first );
            Rinex3ObsStream* pObsStream( it->second );

            try
            {

                streampos sp( pObsStream->tellg() );

                do
                {
                    if( (*pObsStream).peek() != EOF )
                    {
                       (*pObsStream) >> gRin;
                    }
                    else
                    {
                        break;
                    }
                }
                while(gRin.header.epoch < epoch);


                if( gRin.header.epoch == epoch )
                {
                    string antModel = gRin.header.srcAntType ;
                    Antenna antenna;

                    try
                    {
                        antenna = pAntexReader->getAntenna(antModel);
                    }
                    catch(ObjectNotFound& notFound)
                    {
                        // new antenna model
                        if(antModel.size() == 20)
                        {
                            antModel.replace(16,4,"NONE");
                        }
                        else
                        {
                            // Some stations has special format, so we just erase the
                            // last 'NONE' and find the antenna.2017-04-17
                            antModel.erase(antModel.size()-4,4);
                        }

                        // new receiver antenna with new antenna model
                        try
                        {
                            antenna = pAntexReader->getAntenna( antModel );
                        }
                        catch(ObjectNotFound& notFound)
                        {
                            sourceRejectedSet.insert( source );
                            (*pObsStream).close();
                            continue;
                        }
                    }

                    gRin.header.srcAntenna = antenna;

                    gData.addGnssRinex(gRin);

                }
                else if(gRin.header.epoch > epoch )
                {
                    pObsStream->seekg( sp );
                }
            }
            catch(...)
            {
                sourceRejectedSet.insert( source );
                (*pObsStream).close();
                continue;
            }


        }  // End of 'for(...)'

        if(debug)
        {
            cout << "current sourceRinex:" << endl;
            gData.dump(cout,1);
        }

        // remove finished sources
        for(SourceIDSet::const_iterator it = sourceRejectedSet.begin();
            it != sourceRejectedSet.end();
            ++it)
        {
            sourceStreamMap.erase( *it );
        }


    }  // End of method 'NetworkObsStreams::readEpochData()'


      // do some clean operation
    void NetworkObsStreams::cleanUp()
    {
        for(std::list<ObsData>::iterator it = obsDataList.begin();
            it != obsDataList.end();
            ++it)
        {
            // close file error!
            // here, if we close this stream firstly, the error occurred!
            delete it->pObsStream;
            it->pObsStream = (Rinex3ObsStream*)0;
        }

        obsDataList.clear();
        sourceStreamMap.clear();

    }  // End of method 'NetworkObsStreams::cleanUp()'

}  // End of namespace gpstk
